<template>
  <div ref="subassembly" class="v-container">
    <el-input
      ref="input"
      v-model="state.modelValue"
      :placeholder="props.placeholder"
      @keyup.enter.native="keyupEnter"
      @focus="onInputFocus"
      @blur="onInputBlur"
      @change="change"
      @keydown.native.down.stop.prevent="navigateRow('next')"
      @keydown.native.up.stop.prevent="navigateRow('prev')"
      @keydown.native.tab="tabKeyup"
      @input="enterInput"
      :disabled="disabled"
      v-bind="$attrs"
      class="login_input"
      :style="{ backgroundImage: 'url(' + state.gridInputIcon + ')' }"
    >
      <template #prefix>
        <slot name="prefix"></slot>
      </template>
      <template #suffix>
        <slot name="suffix">
          <v-icon :value="props.suffixIcon" @click="triggerIcon" :class="state.visible === false ? '' : 'arrow-up'" />
        </slot>
      </template>
    </el-input>

    <VFloatContainer
      ref="dialog"
      :target="subassembly"
      v-model="state.visible"
      v-if="state.visible"
      :width="props.gridWidth || props.width"
      :height="props.gridHeight || props.height"
    >
      <!-- v-click-outside="onClickOutside" -->
      <div class="flex-column flex-1">
        <div class="filter-search" v-if="hasSearchSlots">
          <slot name="search" :queryParams="state.queryParams" :query="onQuery"></slot>
        </div>
        <el-table
          ref="tableRef"
          :data="state.Data"
          class="flex-1"
          highlight-current-row
          :border="border"
          @current-change="tableHandleChange"
          @row-click="rowclick"
          @row-dblclick="rowdblclick"
          :default-expand-all="defaultExpand"
          :row-style="rowStyle"
          :row-class-name="tableRowClassName"
          :current-row-key="state.index"
          :row-key="rowKey"
          v-loading="state.loading"
          element-loading-text="加载中"
          :tree-props="props.treeProps"
          v-bind="$attrs"
        >
          <el-table-column type="index" label="序号" :width="props.indexWidth" align="center"></el-table-column>
          <!-- 动态生成列-->
          <el-table-column
            v-for="(item, index) in (props.columns.length && props.columns) || props.gridColumn"
            :key="index"
            :prop="item.prop || item.field"
            :label="item.title"
            :width="item.width || ''"
            :show-overflow-tooltip="typeof item.showOverflowTooltip === 'boolean' ? item.showOverflowTooltip : true"
            :align="item.align"
            v-bind="item"
          >
            <template #default="Scope" v-if="item.render">
              <component :is="item.render(Scope, h)"> </component>
            </template>
          </el-table-column>
        </el-table>
      </div>

      <el-pagination
        v-if="props.pagination"
        class="pg-right"
        small
        background
        v-model:current-page="pageNum"
        v-model:page-size="pageSize"
        :page-sizes="pageSizes"
        :layout="layout"
        :total="total"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />

      <!--<el-button @click="state.visible=false" link type="danger" icon="CircleClose" ></el-button>-->
    </VFloatContainer>
  </div>
</template>

<script lang="ts" setup>
  // import type { modelValue, RequestUrl         CombogridProps,} from '../types/index.ts'
  // import type { CombogridProps } from '../types/index'
  import { combogridProps } from './combogridProps'
  import {
    ref,
    reactive,
    getCurrentInstance,
    onBeforeMount, // 在组件挂载之前执行的函数
    onMounted,
    onBeforeUnmount, // 在组件卸载之前执行的函数
    watch,
    watchEffect,
    nextTick,
    ComponentInternalInstance,
    onDeactivated,
    h,
  } from 'vue'

  import VFloatContainer from '../../v-float-container/index'
  import { usePagin } from '@v-model/shared--hooks'
  import { Avatar } from '@element-plus/icons-vue'
  // import { debounce } from 'lodash-es'
  // import vClickOutside from '@/directive/vClickOutside'
  // import { http, tool } from '@v-model/shared--utils'
  import { http, tool, initTree, filterTree } from '@v-model/shared--utils'
  import { VIcon } from '@v-model/shared--modules'
  import { useSlots } from 'vue'
  import vTable from '../../v-table'

  defineOptions({ name: 'v-combogrid' })

  const { proxy } = getCurrentInstance() as ComponentInternalInstance
  const subassembly = ref<HTMLInputElement | null>(null)
  const dialog = ref<HTMLInputElement | null>(null)
  const tableRef = ref(null)
  const input = ref(null)

  const props = defineProps(combogridProps)

  const rend = (params) => {
    return h(params)
  }

  const state = reactive({
    modelValue: '',
    visible: false,
    Data: [],
    tableCache: [], // 将数据源缓存，统一本地和接口获取数据入口
    PageSize: props.pageSizes[0],
    loading: false,
    index: 0,
    queryParams: {}, // 请求接口查询参数
    enterFlag: false,
    pageIndex: 1,
    rowFocus: false,
    gridInputIcon: props.gridInputIcon,
    autoSelect: props.autoSelect, // 当第一次加载时是否默认选中第一条
  })

  // 默认回显传入的展示字段
  if (props.inputlabel) state.modelValue = props.inputlabel as string

  watch(
    () => props.inputlabel,
    (n, o) => {
      state.modelValue = props.inputlabel as string
    }
  )

  const emits = defineEmits([
    'update:modelValue',
    'update:inputlabel',
    'rowClick',
    'rowDblclick',
    'filter',
    'change',
    'current-change',
    'blur',
    'input',
    'options', // 返回查询的数据源
    'next', // 按下键
    'prev', // 按上键
  ])

  const slots = useSlots()

  // 是否自定义 查询区域
  const hasSearchSlots = !!slots.search

  const triggerIcon = () => {
    if (props.disabled) return

    if (state.visible) {
      state.visible = false
    } else {
      onQuery()
    }
  }

  const onQuery = () => {
    // 如果是查询接口，请求接口查找
    state.pageIndex = 1
    if (props.url) {
      requesUrl()
    } else {
      state.index = -1
      const treeArr = JSON.parse(JSON.stringify(state.tableCache))
      // 如果是树，则树查找
      if (props.rowKey) {
        const params = {
          value: state.modelValue,
          data: treeArr,
          prop: props.filterCode || props?.fields[1] || props.label,
          filter: true,
        }
        state.Data = filterTree(params)
      } else {
        // 本地数据过滤
        manualPage()
      }
    }
  }

  //获取分页参数
  const { pageNum, pageSize, total, layout, pageSizes, handleSizeChange, handleCurrentChange } = usePagin(onQuery)
  layout.value = 'sizes, prev, pager, next'

  watch(
    () => props.requestArgs,
    (n, o) => {
      if (JSON.stringify(n) === JSON.stringify(o)) return
      if (state.visible && props.focusQuery) requesUrl()
    }
  )

  // 请求后端接口获取数据
  const requesUrl = (autoSelect: boolean = false) => {
    state.index = -1

    //服务端查询
    let params = {
      ...props.requestArgs,
      pageSize: pageSize.value,
      pageNum: pageNum.value,
      [props.filterCode]: state.modelValue,
      ...state.queryParams,
    }

    state.loading = true
    state.Data = []
    proxy.$http
      .requestJSON('post', props.url, params)
      .then((res: any) => {
        // 返回数据为空时，不进行下面逻辑处理
        // bug  当返回数据为空时，total的值为null会触发 usePagin 中的页数改变方法，会再调用一次请求数据的接口
        if (!res.data) return

        // 处理返回数据结构直接为data或data。records结构
        state.Data = Array.isArray(res.data) ? res.data : res.data.records
        total.value = res.data.total

        // 第一次进入时，只加载一次设置
        if (state.autoSelect && state.Data.length && !props.modelValue) {
          state.autoSelect = false
          rowdblclick(state.Data[0])
        }

        emits('options', state.Data)
      })
      .finally(() => {
        state.loading = false
      })
  }

  //初始化加载列表  是否默认第一个值autoSelect默认
  if (props.initRequesUrl) requesUrl(props.autoSelect)

  // 本地数据分页  查询
  const manualPage = () => {
    const filters = props.fields.length
      ? state.tableCache.filter((it) => it[props.fields[1] || props.label].includes(state.modelValue))
      : state.tableCache
    if (props.pagination) {
      state.Data = filters.slice((pageNum.value - 1) * state.PageSize, (pageNum.value - 1) * state.PageSize + state.PageSize)
    } else {
      state.Data = JSON.parse(JSON.stringify(filters))
      //本地过滤
      emits('filter', state.Data)
    }
  }

  watchEffect(() => {
    const source = JSON.parse(JSON.stringify(props.data))
    // 缓存 数据列表
    if (props.rowKey && props.pid) {
      state.tableCache = initTree(source, props.fields[0] || props.value, props.pid)
    } else {
      state.tableCache = source
    }
    state.Data = JSON.parse(JSON.stringify(state.tableCache))
    total.value = state.tableCache.length // 根据数据分页

    const item = props.data?.length && props.data.find((it) => it[props.fields[0] || props.value] === props.modelValue)
    // 设置显示文本内容
    if (item) {
      state.modelValue = (props.fields[1] || props.label) && item[props.fields[1] || props.label]
    }
  })

  watch(state.tableCache, (n, o) => {
    if (!props.url) pageNum.value = 1
  })

  const change = (val: string | number) => {
    emits('change', val)
  }

  // change 触发请求时，防抖延迟查询
  let timeoutID: number | null = null
  const enterInput = (val: string | number) => {
    if (props.requestEvent.includes('change')) {
      pageNum.value = 1
      if (timeoutID) clearTimeout(timeoutID)
      timeoutID = setTimeout(() => {
        onQuery()
      }, props.delay)
    }
    emits('update:inputlabel', val)
    emits('update:modelValue', '')
    emits('input', val)

    state.enterFlag = false
  }

  const tableRowClassName = ({ rowIndex }: { rowIndex: number }) => {
    return rowIndex === state.index ? 'active-row' : ''
  }

  const rowclick = (row: any, column: any, event: Event) => {
    state.index = state.Data.indexOf(row)
    state.rowFocus = true

    // 单击选择数据
    emits('rowClick', row, column, event)
  }

  const rowdblclick = (row: any, column = null, event = null) => {
    // 自定义控制点击选中行数据, 需要手动设置数据v-model的 key 和 value
    if (props.userDbClick) {
      return emits('rowDblclick', row, column, event, (visible: boolean = false) => (state.visible = visible))
    }

    emits('update:modelValue', row[props.fields[0] || props.value])
    emits('update:inputlabel', row[props.fields[1] || props.label])
    emits('rowDblclick', row, column, event)
    state.modelValue = row[props.fields[1] || props.label]
    state.visible = false
  }

  const tableHandleChange = (val: string | number) => {
    emits('current-change', val)
  }

  //获取焦点
  const onInputFocus = () => {
    if (props.autoVisible) state.visible = true // 输入框聚焦时是否展示下拉表格
    state.rowFocus = false

    if (props.focusQuery) onQuery()
  }

  //失去焦点 关闭弹窗
  const onInputBlur = () => {
    // if (!state.rowFocus && input.value.ref !== document.activeElement) state.visible = false
    emits('blur')
  }

  function clickHandler(e: Event) {
    if (dialog && dialog.value && dialog.value.$el.contains(e.target)) {
      return false
    }
    if (subassembly.value.contains(e.target)) {
      //.contains(e.target)
      return false
    }
    state.visible = false
  }

  function dbclickHandler(e) {
    if (props.disabled) return

    if (dialog && dialog.value && dialog.value.$el.contains(e.target)) {
      return false
    }
    if (subassembly && subassembly.value.contains(e.target)) {
      if (!state.visible) {
        //双击输入框显示
        state.visible = true
      }
    }
  }

  // 点击enter操作
  const keyupEnter = () => {
    if (!state.visible) state.visible = true
    if (!state.enterFlag) {
      pageNum.value = 1
      onQuery()
    }

    // 处理失焦后聚焦其他输入框，弹框未关闭的情况
    const curInput = input.value.input
    nextTick(() => {
      if (curInput !== document.activeElement) state.visible = false
    })

    // 点击enter，选中数据，关闭弹窗
    if (state.index !== -1) {
      const currentRow = state.Data[state.index]

      rowdblclick(currentRow) // 设置选中值
      // input.value.blur()

      // nextTick(() => {
      //   state.visible = false
      // })
    }
  }

  watch(
    () => state.visible,
    (n, o) => {
      if (!n) {
        // 当选中某条数据时，关闭窗口，再次点击enter查询 会选中之前的数据问题修改
        state.index = -1
      }
    }
  )
  //键盘选择
  const navigateRow = (type: string) => {
    if (!state.Data?.length) return

    state.enterFlag = true
    const contentHeight = tableRef.value._.refs.bodyWrapper.offsetHeight // 表格内部的视图高度

    // 根据表格行的高度设置滚动
    const trHeight = tableRef.value._.refs.tableBody.querySelector('tr').clientHeight

    if (type === 'next') {
      // 当为最后一条时不进行操作
      if (state.index === state.Data.length - 1) return

      state.index += 1
      if (trHeight * (state.index + 1) >= contentHeight * state.pageIndex) {
        state.pageIndex++
        if (tableRef.value) tableRef.value.setScrollTop(trHeight * state.index) // 滚动到 state.index 行的位置
      }
      emits('next', state.Data[state.index]) // 点击键盘下键事件
    } else {
      // 上键 ArrowUp e.keyCode == 38

      if (state.index > 0) state.index--
      if (trHeight * (state.index + 1) <= contentHeight * (state.pageIndex - 1)) {
        state.pageIndex--
        if (tableRef.value) tableRef.value.setScrollTop(trHeight * (state.index + 1) - contentHeight) // 滚动到 state.index 行的位置
      }

      emits('prev', state.Data[state.index]) // 点击键盘上键事件
    }
  }

  const tabKeyup = () => {
    if (state.visible) state.visible = false
  }

  const onClickOutside = (el) => {
    console.log(`onClickOutside`, el)
    // state.visible = false
  }
  onBeforeMount(() => {})
  onMounted(() => {
    // // 给当前元素绑定个私有变量，方便在unbind中可以解除事件监听
    subassembly.value.__vueClickOutside__ = clickHandler
    subassembly.value.__vuedbClickOutside__ = dbclickHandler
    document.addEventListener('click', clickHandler)
    document.addEventListener('dblclick', dbclickHandler)
  })

  onDeactivated(() => {
    state.visible = false
  })

  onBeforeUnmount(() => {
    state.visible = false
    //v-float-container
    let boxs = document.querySelectorAll('.v-float-container')
    for (var i = 0; i < boxs.length; i++) {
      boxs[i].parentNode.removeChild(boxs[i])
    }

    // 解除事件监听
    document.removeEventListener('click', subassembly.value.__vueClickOutside__)
    document.removeEventListener('dblclick', subassembly.value.__vuedbClickOutside__)
    delete subassembly.value.__vueClickOutside__
    delete subassembly.value.__vuedbClickOutside__
  })

  defineExpose({ requesUrl })
</script>

<style scoped lang="scss">
  .flex-column {
    display: flex;
    overflow-y: auto;
    flex-direction: column;
  }

  .v-container {
    height: 100%;
    width: 100%;
    position: relative;
  }
  .arrow-up {
    transform: rotate(180deg);
  }
  .filter-search {
    display: flex;
    padding: 4px;
  }

  .pg-right {
    padding: 4px;
    display: flex;
    justify-content: flex-end;
  }
  .login_input {
    background-repeat: no-repeat;
    background-size: 12px;
    background-position: 10px center;
    background-color: #f5f7fa;
    border-radius: 4px;
    padding: 4px;
  }

  :deep(.active-row) {
    background: #cfdae7;
    font-weight: bolder;
  }
</style>
